#include "stdafx.h"
#include "imagex.h"
#include "section.h"
#include "view.h"
#include "proc.h"

namespace xm
{
	long process_t::section_unmap( vxvma_t vma )
	{
		vmarea_t * area = vms.find( vma, -1 );
		if( area->base != vma ) return E_FAIL;
		return section_unmap( area );
	}

	long process_t::section_unmap( vmarea_t * area )
	{
		if( !area || !area->vmso ) 
			return E_UNEXPECTED;
		vxvma_t pba = area->base;
		vxvma_t pbe = area->size + pba;
		for( ; pba < pbe; pba += VM_PAGE_SIZE )
			_pmd_page_free( pba );
		vms.release( area );
		return S_OK;
	}

	vmarea_t * process_t::section_map_image( section_t* sect, size_t opt )
	{
		if( !sect || !sect->ko ) return NULL;

		optr<imagex_t> imgo = sect->ko->proto<imagex_t>();
		if( !imgo ) return NULL;

		size_t can_reloc = false;
		if( opt & IMG_MAP_RELOCATE )
			can_reloc = imgo->relocatable();

		uint64_t want_base = sect->required_base();
		uint64_t want_size = sect->get_size();

		vmarea_t * area = vms.alloc( want_base, want_size, PGA_READ|PGA_EXECUTE|PGA_COMMIT, 0x10000 );		
		if( !area )
		{
			if( !can_reloc ) 
				return NULL;

			area = vms.alloc( 0, want_size, PGA_READ|PGA_EXECUTE|PGA_COMMIT, 0x10000 );
			if( !area ) 
				return NULL;
		}

		optr<image_view_t> view = new image_view_t( *area, this );
		if( !view || view->open( sect, 0 ) < 0 )
		{
			vms.release( area );
			return NULL;
		}

		area->vmso = view.ip<IKernelObject>();
		view->ldopt = opt;

		if( opt & IMG_MAP_PIN_MOD )
		{
			modmgr.on_module_load( view );
			view->mi.loadcount = ( opt & IMG_MAP_STATIC_LOAD ) ? -1 : 1;
		}

		if( opt & IMG_MAP_RELOCATE )
			imgo->relocate( area, this );

		if( opt & IMG_MAP_IMPORT )
			imgo->resolve_imports( area, this );

		return area;
	}

	vmarea_t * process_t::section_map_file( section_t* sect, uint64_t offset, uint64_t length, uint32_t fmda, vxvma_t base )
	{
		uint64_t sectlen = sect->get_size();
		if( sectlen == LENGTH_ERROR ) return NULL;
		if( length > sectlen )
			return NULL;
		if( offset >= sectlen ) 
			return NULL;
		if( offset + length > sectlen )
			return NULL;

		//////////////////////////////////////////////////////////////////////////
		//
		// FIXME: access check
		//

		uint32_t prot = sect->GetAccess();
		uint32_t paga = fma_to_paga( fmda ) | PGA_COMMIT;

		//////////////////////////////////////////////////////////////////////////

		uint64_t want_base = sect->required_base();
		if( !want_base ) want_base = base;

		vmarea_t * area = vms.alloc( want_base, length, paga, 0x10000 );		
		if( !area ) return NULL;

		optr<file_view_t> view = new file_view_t( *area, this );
		if( !view ) 
		{
			vms.release( area );
			return NULL;
		}

		long lr = view->open( sect, offset, length );
		if( lr < 0 )
		{
			vms.release( area );
			return NULL;
		}

		area->vmso = view.ip<IKernelObject>();
		return area;
	}

	long process_t::relocate( vmarea_t * area, uint32_t rva, uint32_t paga, int64_t diff, const uint16_t * offsetp, size_t num, void * raw_page )
	{
		uint8_t * page = new uint8_t[VM_PAGE_SIZE];//(uint8_t *)_private_alloc( pvma, paga );
		if( !page ) return E_FAIL;

		memcpy( page, raw_page, VM_PAGE_SIZE );

		size_t pgcb = VM_PAGE_SIZE;

		int32_t tmp = 0;

		for( size_t i = 0; i < num; ++ i )
		{
			uint16_t ri = offsetp[i];
			if( !ri ) break; 

			size_t fixoff = ri & VM_PAGE_MASK;

			uint8_t * fixptr = page + fixoff;

			switch (ri >> 12) 
			{
			case IMAGE_REL_BASED_HIGHLOW :
				if( fixoff + sizeof(uint32_t) <= pgcb )
				{
					*(int32_t*)fixptr += (uint32_t)diff;
				}

				break;
			case IMAGE_REL_BASED_HIGH :

				if( fixoff + sizeof(uint16_t) <= pgcb )
				{
					tmp = (*(uint16_t*)fixptr) << 16;
					tmp += (uint32_t)diff;
					*(uint16_t*)fixptr = (uint16_t)(tmp >> 16);
				}
				break;
			case IMAGE_REL_BASED_HIGHADJ :
				break;
			case IMAGE_REL_BASED_LOW :
				if( fixoff + sizeof(uint16_t) <= pgcb )
				{
					tmp = *(int16_t*)fixptr;
					tmp += (uint32_t)diff;
					*(uint16_t*)fixptr = (uint16_t)tmp;
				}
				break;
			case IMAGE_REL_BASED_DIR64:
				if( fixoff + sizeof(uint64_t) <= pgcb )
				{
					*(uint64_t*)(fixptr) += diff;
				}
				break;
			case IMAGE_REL_BASED_ABSOLUTE :
				break;
			default :
				return E_FAIL;
			}
		}
		return S_OK;
	}
};